/*==============================================================================
 *
 * Chris Rigg and Sam Strom
 *
 * ECEN 5033
 *
 * Spring 2008
 *
 */
#include "common_headers.h"
#include "frame_parse.h"

/********************************************************************
 *
 * Swap the endianness of an integer
 *
 *******************************************************************/
unsigned int endian_swap(unsigned int x)
{
    x = (x>>24) |
        ((x<<8) & 0x00FF0000) |
        ((x>>8) & 0x0000FF00) |
        (x<<24);

    return x;
}

/********************************************************************
 *
 * Filter all bytes to 0x00 if they are not between lower and upper
 *
 *******************************************************************/
void filter(unsigned char *buffer, int width, int height, unsigned char lower, unsigned char upper) {
  int i;

  for(i = 0 ; i < width * height * 4; i++) {
    if((unsigned char)buffer[i] < (unsigned char)lower || (unsigned char)buffer[i] > (unsigned char)upper) buffer[i] = 0x00;
  }
}

/********************************************************************
 *
 * Filter out all but one color, leave the bytes for the other color
 * as 0x00 so they will have not contribution (black in places where
 * color also has no contribution)
 *
 *******************************************************************/
void filter_color(int *buffer, int width, int heigth, int color) {
  int i;
  int val;
  unsigned int mask;

  switch(color) {
    case RED:
      mask = 0x0000FF00;
      break;
    case GREEN:
      mask = 0x00FF0000;
      break;
    case BLUE:
      mask = 0xFF000000;
      break;
    default: // white by default
      mask = 0xFFFFFF00;
      break;
  }

  // filter the color
  for(i = 0; i < ( width * heigth ); i++) {
    // or it to set bits and keep some bits
    buffer[i] &= mask;
  }
}

/********************************************************************
 *
 * Given ranges for each color, find the average (x,y) position for
 * those pixels, set x and y accordingly
 *
 *******************************************************************/
double find_center(void *buffer,  unsigned char r_l, unsigned char r_h,
                                unsigned char g_l, unsigned char g_h,
                                unsigned char b_l, unsigned char b_h,
                    int width, int height, int *x, int *y) {
    int i, j;
    int total_x, total_y, count;
    long val;
    unsigned char temp;

    total_x = 0; total_y = 0; count = 0;
    for(i = 0; i < width * height; i++) {
        temp = 0;
        val = endian_swap(((long*)buffer)[i]);

        // red
        temp = (unsigned char)((val & 0x00ff0000) >> 16);
        if(!((temp >= r_l) && (temp <= r_h))) continue;
        // green
        temp = (unsigned char)((val & 0x0000ff00) >> 8);
        if(!((temp >= g_l) && (temp <= g_h))) continue;
        // blue
        temp = (unsigned char)(val & 0x000000ff);
        if(!((temp >= b_l) && (temp <= b_h))) continue;

        count++;
        total_x += i % width;
        total_y += (int) (i / width);
    }

    if(count == 0) {
        *x = 0;
        *y = 0;
    } else {
        *x = (int) (total_x / count);
        *y = (int) (total_y / count);
    }

    return (double) count / (double) (width * height);
}

/********************************************************************
 *
 * A crosshairs to the frame which will meet at (x_cross,y_cross)
 *
 *******************************************************************/
void add_crosshairs(int *buffer, int width, int height, int x_cross, int y_cross) {
    int i;

    // add crosshairs - x
    for(i = x_cross; i < width * height; i += width) {
      buffer[i] = 0x0000ff00;
    }

    // add crosshairs - y
    for(i = y_cross * width; i < y_cross * width + width; i++) {
      buffer[i] = 0x0000ff00;
    }

    // put a box around the target area
    for(i = (width * ((height / 2) - CENTER_OFFSET)) + ((width / 2) -
            CENTER_OFFSET);
        i <= (width * ((height / 2) + CENTER_OFFSET)) +
            ((width / 2) - CENTER_OFFSET);
        i += width)
    {
      buffer[i] = 0x0000ff00;
      buffer[i+CENTER_OFFSET * 2] = 0x0000ff00;
    }

    for(i = (width * ((height / 2) - CENTER_OFFSET)) + ((width / 2) -
            CENTER_OFFSET);
        i <= (width * ((height / 2) - CENTER_OFFSET)) +
            ((width / 2) + CENTER_OFFSET);
        i++)
    {
      buffer[i] = 0x0000ff00;
      buffer[i + CENTER_OFFSET * 2 * width] = 0x0000ff00;
    }
}

/********************************************************************
 *
 * Save a frame to a ppm file named /tmp/frames/dump_<>.ppm where <>
 * is a number that is specified by id
 *
 *******************************************************************/
void save_frame (const void *data, int width, int height, int id)
{
  int i;
  int val;
  char filename[50];
  FILE *save_ppm;

  sprintf(filename, "/tmp/frames/dump_%d.ppm", id);
  printf("Dump the frame - %s\n", filename);
  save_ppm = fopen(filename, "w+");

  if(save_ppm == NULL) {
    printf("ERROR: cannot dump file\n");
    return;
  }

  // Write PNM header
  fprintf( save_ppm, "P6\n" );
  fprintf( save_ppm, "# Generated by a herd of rabid fleas\n" );
  fprintf( save_ppm, "%d %d\n", WIDTH, HEIGHT );
  fprintf(save_ppm, "255\n");

  // Write image data
  for ( i = 0; i < ( width * height); i++ )
  {
    // Retrieve lower 24 bits of ARGB
    val = endian_swap (((int*)data)[i]);

    fprintf( save_ppm, "%c%c%c",
            ( val & 0x00ff0000 ) >> 16, // Red
            ( val & 0x0000ff00 ) >>  8, // Green
            ( val & 0x000000ff )        // Blue
           );
  }
  fclose(save_ppm);
}

/********************************************************************
 *
 * Save a frame in a readable format - each pixel will be on its
 * own line
 *
 *******************************************************************/
void save_frame_readable (const void *data, int width, int height, int id)
{
  int i, j;
  int val;
  char filename[50];
  FILE *save_ppm;

  sprintf(filename, "/tmp/frames/readable_%d.out", id);
  printf("Dump the frame in readable form - %s\n", filename);
  save_ppm = fopen(filename, "w+");

  if(save_ppm == NULL) {
    printf("ERROR: cannot dump file\n");
    return;
  }

  // Write image data
  for(i = 0; i < height; i++) {
    fprintf(save_ppm, "ROW %d -----------------------------------\n", i);
    for(j = 0; j < width; j++) {
      // Retrieve lower 24 bits of ARGB
      val = endian_swap (((int*)data)[i*width + j]);
  
      fprintf( save_ppm, "(%3d,%3d) %02x %02x %02x\n",
                i, j,
                ( val & 0x00ff0000 ) >> 16, // Blue
                ( val & 0x0000ff00 ) >>  8, // Green
                ( val & 0x000000ff )        // Red
             );
    }
  }
  fclose(save_ppm);
}


/*==============================================================================
 *
 * Old functions used in development
 * 
 */


/*
void get_red_buffer(char *buffer, int width, int height) {
    int i;
    // write out all red
    for(i = 0; i < width*height*3; i+=3) {
        sprintf(&buffer[i], "%c%c%c", 0xff, 0x00, 0x00);
    }
}
*/

/*
void add_block(char *buffer, int width, int height, int x_start, int y_start, int b_width, int b_height) {
    int i, j;

    y_start = 0; x_start = 0;
    b_width = 320; b_height = 240;

    for(i = y_start*height; i < b_height; i++) {
        for(j = x_start*3; j < b_width*3; j++) {
            sprintf(&buffer[i*width*3 + j], "%c%c%c", 0x00, 0x00, 0x00);
        }
    }

}
*/

/*
void read_ppm(char *buffer, char *filename, int width, int height) {
    FILE *file;
    char line[1024];
    int i, j;

    // open the file for read
    file = fopen(filename, "r");
    if(file == NULL) {
        printf("ERROR: can't open file for read\n");
        exit(1);
    }

    fgets(line, 1024, file);    
    fgets(line, 1024, file);    
    fgets(line, 1024, file);    
    fgets(line, 1024, file);
    fread(buffer, 1, width*height*3, file);

    for(i = 0; i < height; i++) {
        for(j = 0; j < 150; j++) {
            buffer[i*width*3 + j] = 0xff;
        }
    }
}
*/
/*
void write_ppm(char *buffer, char *filename, int width, int height) {
    FILE *file;
    
    // open the file for write
    file = fopen(filename, "w");
    if(file == NULL) {
        printf("ERROR: can't open file for write\n");
        exit(1);
    }

    fprintf(file, "P6\n");
    fprintf(file, "# test.ppm\n");
    fprintf(file, "%d %d\n", width, height);
    fprintf(file, "255\n");
    // write out to file
    fwrite(buffer, 1, width*height*3, file);

    // close the file
    fclose(file);
}
*/

/* main function */
/*
int main(int argc, char **argv) {

    int x, y;
    char buffer[HEIGHT*WIDTH*3];

    // write out all red file
    get_red_buffer(buffer, WIDTH, HEIGHT);
    write_ppm(buffer, "test.ppm", WIDTH, HEIGHT);

    // add the crosshairs and write
    add_crosshairs(buffer, WIDTH, HEIGHT, WIDTH / 2, HEIGHT / 2);
    write_ppm(buffer, "test_cross.ppm", WIDTH, HEIGHT);

    // re-get the buffer and add a block to the upper left corner
    get_red_buffer(buffer, WIDTH, HEIGHT);
    add_block(buffer, WIDTH, HEIGHT, 0, 0, WIDTH / 2, HEIGHT / 2);
    write_ppm(buffer, "test_block.ppm", WIDTH, HEIGHT);

    // find the center of the black
    find_center(buffer, 0x00000000, WIDTH, HEIGHT, &x, &y);
    printf("found (%d,%d)\n", x, y);
    add_crosshairs(buffer, WIDTH, HEIGHT, x, y);
    write_ppm(buffer, "test_find.ppm", WIDTH, HEIGHT);

    // read a file and write it out to a new file
    read_ppm(buffer, "test_find.ppm", WIDTH, HEIGHT);
    write_ppm(buffer, "test_read.ppm", WIDTH, HEIGHT);

    read_ppm(buffer, "out.pnm", WIDTH, HEIGHT);
    find_center(buffer, 0x00FFFFFF, WIDTH, HEIGHT, &x, &y);
    add_crosshairs(buffer, WIDTH, HEIGHT, x, y);
    write_ppm(buffer, "example_find.ppm", WIDTH, HEIGHT);
    
    return 0;
}
*/
